﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace SpectationClient.DataBaseDescription {
    public class ForeignKey {
        private TableInfo sourceTable;
        private TableInfo targetTable;
        private List<ColumnInfo> sourceColumns;
        private List<ColumnInfo> targetColumns;

        private static Dictionary<Type, List<Type>> typeCasts = new Dictionary<Type, List<Type>>() {
            { typeof(decimal), new List<Type> { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(char) } },
            { typeof(double), new List<Type> { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(char), typeof(float) } },
            { typeof(float), new List<Type> { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(char), typeof(float) } },
            { typeof(ulong), new List<Type> { typeof(byte), typeof(ushort), typeof(uint), typeof(char) } },
            { typeof(long), new List<Type> { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(char) } },
            { typeof(uint), new List<Type> { typeof(byte), typeof(ushort), typeof(char) } },
            { typeof(int), new List<Type> { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(char) } },
            { typeof(ushort), new List<Type> { typeof(byte), typeof(char) } },
            { typeof(short), new List<Type> { typeof(byte) } }
        };

        public ForeignKey(List<ColumnInfo> sourceColumns, List<ColumnInfo> targetColumns) {
            //Check the input
            if(sourceColumns.Count != targetColumns.Count) throw new Exception("Number of appendix- and target columns does not match");
            if(!ForeignKey.belongToSameTable(sourceColumns)) throw new Exception("Columns must belong to same SchemaInfo");
            if(!ForeignKey.belongToSameTable(targetColumns)) throw new Exception("Columns must belong to same SchemaInfo");
            
            this.init(sourceColumns, targetColumns);
        }


        public ForeignKey(TableInfo sourceTable, String[] sourceColumns, 
                          TableInfo targetTable, String[] targetColumns){
            //Check the input
            if(sourceColumns.Length != targetColumns.Length) throw new Exception("Number of appendix- and target columns does not match");
            List<ColumnInfo> srcColumns = sourceTable.getColumns(sourceColumns);
            List<ColumnInfo> tarColumns = targetTable.getColumns(targetColumns);
            this.init(srcColumns, tarColumns);
        }

        private void init(List<ColumnInfo> sourceColumns, List<ColumnInfo> targetColumns){
            ForeignKey.testForMatchingDataTypes(sourceColumns, targetColumns);
            this.sourceTable = sourceColumns[0].Table;
            this.targetTable = targetColumns[0].Table;

            this.sourceColumns = sourceColumns;
            this.targetColumns = targetColumns;
        }

        public List<ColumnInfo> SourceColumns { get { return this.sourceColumns; } }
        public List<ColumnInfo> TargetColumns { get { return this.targetColumns; } }

        public TableInfo SourceTable { get { return this.sourceColumns[0].Table; } }
        public TableInfo TargetTable { get { return this.targetColumns[0].Table; } }

        public override string ToString() {
            return String.Format("FK: {0}({1}) -> {2}({3})",
                this.sourceTable.Name,
                TextHelper.combine(this.SourceColumns.Select(p => p.Name).ToArray(), ", "),
                this.targetTable.Name,
                TextHelper.combine(this.TargetColumns.Select(p => p.Name).ToArray(), ", "));

        }

        private static bool belongToSameTable(List<ColumnInfo> columns) {
            TableInfo ti1 = columns[0].Table;
            foreach(ColumnInfo c in columns) {
                if(c.Table != ti1) return false;
            }
            return true;
        }

        private static void testForMatchingDataTypes(List<ColumnInfo> sourceColumns, List<ColumnInfo> targetColumns) {
                
            for(int i=0; i< sourceColumns.Count; i++) {
                
                Type ts = sourceColumns[i].ValueType;
                Type tt = targetColumns[i].ValueType;

                if(tt == ts){
                    continue;
                }else if(ForeignKey.typeCasts.ContainsKey(tt) && 
                         ForeignKey.typeCasts[tt].Contains(ts)) {
                             continue;
                }else{
                    if(!tt.IsAssignableFrom(ts)){
                        throw new Exception(String.Format("SourceColumn {0} and TargetColumn {1} have different data types.",
                                sourceColumns[i].Name, targetColumns[i].Name));
                    }
                }
                

                /*
                if(sourceColumns[i].ValueType != targetColumns[i].ValueType)
                    Stuff.DataHelper.CanConvert
                    throw new Exception(String.Format("SourceColumn {0} and TargetColumn {1} have different data types.",
                        sourceColumns[i].Name, targetColumns[i].Name));
                */
            }

        }


    }
}
